package com.qboot.generate.utils;

import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qboot.common.consts.CommonConst;
import com.qboot.common.consts.NumberConst;
import com.qboot.common.utils.AssertUtils;
import com.qboot.generate.consts.GenerateConst;
import com.qboot.generate.entity.GenTable;
import com.qboot.generate.entity.GenTableColumn;
import com.qboot.generate.mapper.GenTableColumnMapper;
import com.qboot.generate.mapper.GenTableMapper;
import com.qboot.generate.vo.GeneratePathVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.util.CollectionUtils;

import java.io.StringWriter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Velocity 模板工具类
 *
 * @author Tellsea
 * @date 2022/11/25
 */
public class VelocityUtils {

    static {
        Properties properties = new Properties();
        properties.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(properties);
    }

    /**
     * 表批量生成
     *
     * @param entity
     */
    public static void generate(GeneratePathVo entity) {
        AssertUtils.isTrue(CollectionUtils.isEmpty(entity.getTableIdList()), "表ID不能为空");
        for (Long tableId : entity.getTableIdList()) {
            generate(tableId, entity);
        }
    }

    /**
     * 代码生成
     *
     * @param tableId
     * @param config
     * @return
     */
    public static Map<String, String> generate(Long tableId, GeneratePathVo config) {
        GenTableMapper genTableMapper = SpringUtil.getBean(GenTableMapper.class);
        GenTableColumnMapper genTableColumnMapper = SpringUtil.getBean(GenTableColumnMapper.class);

        GenTable genTable = genTableMapper.selectById(tableId);
        List<GenTableColumn> genTableColumnList = genTableColumnMapper.selectList(new LambdaQueryWrapper<GenTableColumn>()
                .eq(GenTableColumn::getTableId, tableId));
        List<GenTableColumn> columnList = genTableColumnList.stream()
                .filter(e -> !GenerateConst.BASE_ENTITY_LIST.contains(e.getJavaField()))
                .collect(Collectors.toList());
        long isLogicDelete = columnList.stream().filter(e -> Objects.equals(e.getIsLogicDelete(), NumberConst.ONE)).count();
        List<String> packageList = columnList.stream()
                .filter(e -> StrUtil.isNotEmpty(e.getJavaPackage()))
                .map(GenTableColumn::getJavaPackage)
                .distinct()
                .collect(Collectors.toList());
        // 数据
        VelocityContext context = new VelocityContext();
        context.put("table", genTable);
        context.put("genTableColumnList", genTableColumnList);
        context.put("columnList", columnList);
        context.put("columnCount", isLogicDelete > 0 ? columnList.size() -1 : columnList.size());
        context.put("packageList", packageList);
        context.put("isLogicDelete", isLogicDelete);
        context.put("date", DateUtil.format(DateUtil.date(), "yyyy-MM-dd"));

        // 前端与模板冲突的标签
        context.put("ref", "$refs");
        context.put("message", "this.$message");

        // 模板
        Map<String, String> templateMap = new HashMap<>(16);
        String javaPath = "/" + genTable.getMavenModule() + "/src/main/java/" + genTable.getParentPackage().replace(".", "/") + "/" + genTable.getModule();
        templateMap.put("/vm/java/entity.java.vm", javaPath + "/entity/" + genTable.getEntity() + ".java");
        templateMap.put("/vm/java/entityVo.java.vm", javaPath + "/vo/" + genTable.getEntityVo() + ".java");
        templateMap.put("/vm/java/controller.java.vm", javaPath + "/controller/" + genTable.getController() + ".java");
        templateMap.put("/vm/java/service.java.vm", javaPath + "/service/" + genTable.getService() + ".java");
        templateMap.put("/vm/java/serviceImpl.java.vm", javaPath + "/service/impl/" + genTable.getServiceImpl() + ".java");
        templateMap.put("/vm/java/mapper.java.vm", javaPath + "/mapper/" + genTable.getMapper() + ".java");
        templateMap.put("/vm/java/mapper.xml.vm", "/" + genTable.getMavenModule() + "/src/main/resources/mapper/" + genTable.getModule() + "/" + genTable.getMapperXml() + ".xml");
        if (CompareUtil.compare(genTable.getIsPc(), 1) == 0) {
            templateMap.put("/vm/sql/sql.sql.vm", "/" + genTable.getMavenModule() + "/sql/" + genTable.getEntity() + ".sql");
            templateMap.put("/vm/antdvue/api/js.js.vm", "/" + genTable.getMavenModule() + "/vue/api/" + genTable.getModule() + "/" + genTable.getBusiness() + ".js");
            templateMap.put("/vm/antdvue/views/index.vue.vm", "/" + genTable.getMavenModule() + "/vue/views/" + genTable.getModule() + "/" + genTable.getBusiness() + "/index.vue");
            if (CompareUtil.compare(genTable.getIsSave(), 1) == 0 || CompareUtil.compare(genTable.getIsUpdate(), 1) == 0) {
                templateMap.put("/vm/antdvue/views/modules/CreateForm.vue.vm", "/" + genTable.getMavenModule() + "/vue/views/" + genTable.getModule() + "/" + genTable.getBusiness() + "/modules/CreateForm.vue");
            }
            if (CompareUtil.compare(genTable.getIsImport(), 1) == 0) {
                templateMap.put("/vm/antdvue/views/modules/ImportForm.vue.vm", "/" + genTable.getMavenModule() + "/vue/views/" + genTable.getModule() + "/" + genTable.getBusiness() + "/modules/ImportForm.vue");
            }
        }
        if (config.getType() == 0) {
            Map<String, String> fileMap = new HashMap<>(16);
            for (Map.Entry<String, String> entry : templateMap.entrySet()) {
                String newFilePath = CommonConst.GENERATE_PATH + entry.getValue();
                FileUtil.touch(newFilePath);
                StringWriter writer = new StringWriter();
                Velocity.getTemplate(entry.getKey(), "UTF-8").merge(context, writer);
                FileUtil.writeString(writer.toString(), FileUtil.file(newFilePath), CharsetUtil.UTF_8);
                fileMap.put(StringUtils.substringAfterLast(entry.getKey(), "/").replace(".vm", ""), writer.toString());
            }
            return fileMap;
        } else if (config.getType() == NumberConst.ONE) {
            // 生成到默认路径
            for (Map.Entry<String, String> entry : templateMap.entrySet()) {
                String newFilePath = CommonConst.GENERATE_PATH + entry.getValue();
                FileUtil.touch(newFilePath);
                StringWriter writer = new StringWriter();
                Velocity.getTemplate(entry.getKey(), "UTF-8").merge(context, writer);
                FileUtil.writeString(writer.toString(), FileUtil.file(newFilePath), CharsetUtil.UTF_8);
            }
        } else if (config.getType() == NumberConst.TWO) {
            // 生成到项目
            for (Map.Entry<String, String> entry : templateMap.entrySet()) {
                String newFilePath = "";
                if (StrUtil.startWith(entry.getKey(), "/vm/java")) {
                    newFilePath = config.getServicePath();
                } else if (StrUtil.startWith(entry.getKey(), "/vm/vue3/vue")) {
                    newFilePath = config.getPcPath();
                } else {
                    newFilePath = CommonConst.GENERATE_PATH;
                }
                newFilePath += entry.getValue();
                FileUtil.touch(newFilePath);
                StringWriter writer = new StringWriter();
                Velocity.getTemplate(entry.getKey(), "UTF-8").merge(context, writer);
                FileUtil.writeString(writer.toString(), FileUtil.file(newFilePath), CharsetUtil.UTF_8);
            }
        }
        return null;
    }
}
